package com.gitee.wsl.logger.context

import com.gitee.wsl.logger.LogLevel
import com.gitee.wsl.logger.MDCLogger
import com.gitee.wsl.logger.withMDC
import kotlin.coroutines.AbstractCoroutineContextElement
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.coroutineContext

class CoroutineLogContext(
    data: MutableLogContext = MutableLogContext(),
) : AbstractCoroutineContextElement(Key), MutableLogContext by data {

    companion object Key : CoroutineContext.Key<CoroutineLogContext> {
        @JvmStatic
        suspend fun get(): CoroutineLogContext = coroutineContext[CoroutineLogContext] ?: CoroutineLogContext(
            ThreadLogContext()
        )
    }
}

internal suspend fun MDCLogger.sLog(
    level: LogLevel,
    message: String,
    throwable: Throwable? = null,
    meta: Map<String, Any> = emptyMap(),
    context: LogContext? = null,
) {
    val coroutineLogContext = CoroutineLogContext.get().snap()

    log(level, message,throwable,coroutineLogContext + context + meta)
}

suspend fun MDCLogger.sError(message: String, throwable: Throwable? = null, meta: Map<String, Any> = emptyMap()) {
    sLog(LogLevel.ERROR, message, throwable, meta)
}

suspend fun MDCLogger.sWarning(message: String, throwable: Throwable? = null, meta: Map<String, Any> = emptyMap()) {
    sLog(LogLevel.WARNING, message, throwable, meta)
}

suspend fun MDCLogger.sInfo(message: String, throwable: Throwable? = null, meta: Map<String, Any> = emptyMap()) {
    sLog(LogLevel.INFO, message, throwable, meta)
}

suspend fun MDCLogger.sDebug(message: String, throwable: Throwable? = null, meta: Map<String, Any> = emptyMap()) {
    sLog(LogLevel.DEBUG, message, throwable, meta)
}

suspend fun <T> MDCLogger.withLogContext(context: Map<String, Any>? = null, body: suspend (MDCLogger) -> T): T {
    val logContext = CoroutineLogContext.get()

    if (context != null) {
        logContext.putAll(context)
    }

    return withMDC(logContext,body)
}